package com.george.plugins.jira;

import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;

import com.atlassian.jira.rpc.soap.client.JiraSoapService;
import com.atlassian.jira.rpc.soap.client.RemoteAuthenticationException;
import com.atlassian.jira.rpc.soap.client.RemotePermissionException;
import com.atlassian.jira.rpc.soap.client.RemoteVersion;

/**
 * Goal that creates a version in a JIRA project . NOTE: SOAP access must be
 * enabled in your JIRA installation. Check JIRA docs for more info.
 * 
 * @goal release-jira-version
 * @phase deploy
 * 
 * @author George Gastaldi
 */
public class JiraVersionMojo extends AbstractJiraMojo {

	/**
	 * Released Version
	 * 
	 * @parameter expression="${releaseVersion}"
	 *            default-value="${releaseVersion}"
	 */
	String releaseVersion;

	/**
	 * Next Development Version
	 * 
	 * @parameter expression="${developmentVersion}"
	 *            default-value="${project.version}"
	 * @required
	 */
	String developmentVersion;

	/**
	 * Auto Discover latest release and release it.
	 * 
	 * @parameter expression="${autoDiscoverLatestRelease}" default-value="true"
	 */
	boolean autoDiscoverLatestRelease;
	
	
	/**
	 * Comparator for discovering the latest release
	 * @parameter implementation="com.george.plugins.jira.RemoteVersionComparator" 
	 */
	Comparator<RemoteVersion> remoteVersionComparator = new RemoteVersionComparator();
	
	public void execute() throws MojoExecutionException, MojoFailureException {
		Log log = getLog();
		try {
			JiraSoapService jiraService = getJiraSoapService();
			loadUserInfoFromSettings();
			String loginToken = jiraService.login(jiraUser, jiraPassword);
			try {
				log.debug("Login Token returned: " + loginToken);
				RemoteVersion[] versions = jiraService.getVersions(loginToken,
						jiraProjectKey);
				String thisReleaseVersion = (autoDiscoverLatestRelease) ? calculateLatestReleaseVersion(versions) : releaseVersion;
				if (thisReleaseVersion != null) {
					markVersionAsReleased(jiraService, loginToken, versions, thisReleaseVersion);
				}
				if (developmentVersion != null) {
					// Removing -SNAPSHOT suffix for safety
					String newDevVersion = developmentVersion.replace(
							"-SNAPSHOT", "");
					boolean versionExists = isVersionAlreadyPresent(versions,
							newDevVersion);
					if (!versionExists) {
						RemoteVersion newVersion = new RemoteVersion();
						log.debug("New Development version in JIRA is: "
								+ newDevVersion);
						newVersion.setName(newDevVersion);
						jiraService.addVersion(loginToken, jiraProjectKey,
								newVersion);
						log.info("Version created in JIRA for project key "+jiraProjectKey+" : "
								+ newDevVersion);
					} else {
						log
								.warn(String
										.format(
												"Version %s is already created in JIRA. Nothing to do.",
												newDevVersion));
					}
				}
			} finally {
				log.debug("Logging out from JIRA");
				jiraService.logout(loginToken);
				log.debug("Logged out from JIRA");
			}
		} catch (Exception e) {
			log.error("Error when executing mojo", e);
			// XXX: Por enquanto nao faz nada.
		}
	}

	/**
	 * Returns the latest unreleased version
	 * 
	 * @param versions
	 * @return
	 */
	String calculateLatestReleaseVersion(RemoteVersion[] versions) {
		Arrays.sort(versions, remoteVersionComparator);
		
		for (RemoteVersion remoteVersion : versions) {
			if (!remoteVersion.isReleased())
				return remoteVersion.getName();
		}
		return null;
	}

	/**
	 * Check if version is already present on array
	 * 
	 * @param versions
	 * @param newDevVersion
	 * @return
	 */
	boolean isVersionAlreadyPresent(RemoteVersion[] versions,
			String newDevVersion) {
		boolean versionExists = false;
		if (versions != null) {
			// Creating new Version (if not already created)
			for (RemoteVersion remoteVersion : versions) {
				if (remoteVersion.getName().equalsIgnoreCase(newDevVersion)) {
					versionExists = true;
					break;
				}
			}
		}
		// existant
		return versionExists;
	}

	/**
	 * Release Version
	 * 
	 * @param log
	 * @param jiraService
	 * @param loginToken
	 * @throws RemoteException
	 * @throws RemotePermissionException
	 * @throws RemoteAuthenticationException
	 * @throws com.atlassian.jira.rpc.soap.client.RemoteException
	 */
	void markVersionAsReleased(JiraSoapService jiraService, String loginToken,
			RemoteVersion[] versions, String releaseVersion) throws RemoteException,
			RemotePermissionException, RemoteAuthenticationException,
			com.atlassian.jira.rpc.soap.client.RemoteException {
		if (versions != null) {
			for (RemoteVersion remoteReleasedVersion : versions) {
				if (releaseVersion.equalsIgnoreCase(remoteReleasedVersion
						.getName())
						&& !remoteReleasedVersion.isReleased()) {
					// Mark as released
					remoteReleasedVersion.setReleased(true);
					remoteReleasedVersion
							.setReleaseDate(Calendar.getInstance());
					jiraService.releaseVersion(loginToken, jiraProjectKey,
							remoteReleasedVersion);
					getLog().info(
							"Version " + remoteReleasedVersion.getName()
									+ " was released in JIRA.");
					break;
				}
			}
		}
	}
}
